package com.lx.util.secure.algorithm;

import com.lx.annotation.Note;
import com.lx.util.LX;
import com.lx.util.secure.Algorithm;
import com.lx.util.secure.algorithm.coder.CoderType;
import com.lx.util.secure.algorithm.coder.Encoder;
import com.lx.util.secure.algorithm.coder.Url;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.*;

public abstract class SecureAlgorithm<T extends SecureAlgorithm> implements Algorithm {
    @Note("安全秘钥 对称加密时使用")
    private byte[] key;

    @Note("是否为公钥")
    private Boolean pubKey;

    @Note("加密算法的初始向量")
    private byte[] iv;

    @Note("加密算法")
    private String encryptAlgorithm;

    @Note("byte[]转换字符串")
    private CoderType coderType;

    @Note("byte[]转换字符串")
    private CoderType keyCoderType;

    public SecureAlgorithm() {
        setEncryptAlgorithm(defaultEncryptAlgorithm());
        setCoderType(LX.ifNull(defaultCoderType(), CoderType.DEFAULT));
        setKeyCoderType(LX.ifNull(defaultKeyCoderType(), CoderType.DEFAULT));
    }

    protected abstract CoderType defaultKeyCoderType();
    protected abstract CoderType defaultCoderType();
    protected abstract String defaultEncryptAlgorithm();

    protected byte[] getKey() {
        return key;
    }

    protected void setKey(byte[] key) {
        this.key = key;
    }

    protected Boolean getPubKey() {
        return pubKey;
    }

    protected void setPubKey(Boolean pubKey) {
        this.pubKey = pubKey;
    }

    protected byte[] getIv() {
        if (iv != null){
            return iv;
        }else if (key.length>16){
            // IV 长度不能超过16
            return Arrays.copyOf(key, 16);
        }else{
            return key;
        }
    }

    protected void setIv(byte[] iv) {
        this.iv = iv;
    }

    protected String getEncryptAlgorithm() {
        return encryptAlgorithm;
    }

    protected void setEncryptAlgorithm(String encryptAlgorithm) {
        this.encryptAlgorithm = encryptAlgorithm;
    }

    protected CoderType getCoderType() {
        return coderType;
    }

    protected void setCoderType(CoderType coderType) {
        this.coderType = coderType;
    }

    public CoderType getKeyCoderType() {
        return keyCoderType;
    }

    public void setKeyCoderType(CoderType keyCoderType) {
        this.keyCoderType = keyCoderType;
    }

    public T pubKey(boolean pubKey){
        this.pubKey = pubKey;
        return (T) this;
    }
    public T key(byte[] key){
        this.key = key;
        return (T) this;
    }
    public T key(String keyStr){
        this.key = keyCoderType.decode(keyStr);
        return (T) this;
    }
    public T key(String keyStr, CoderType coderType){
        this.key = coderType.decode(keyStr);
        return (T) this;
    }
    public T iv(byte[] iv){
        this.iv = iv;
        return (T) this;
    }
    public T encryptAlgorithm(String encryptAlgorithm){
        this.encryptAlgorithm = encryptAlgorithm;
        return (T) this;
    }

    public T coderType(CoderType coderType){
        this.coderType = coderType;
        return (T) this;
    }

    public abstract byte[] encrypt(byte[] bytes);

    public abstract byte[] decrypt(byte[] bytes);

    @Note("自选编码格式")
    public Encoder encryptToEncoder(byte[] bytes){
        return new Encoder(encrypt(bytes));
    }

    @Note("自选编码格式")
    public Encoder encryptToEncoder(String data){
        return new Encoder(encrypt(data.getBytes(StandardCharsets.UTF_8)));
    }

    @Note("使用coderType对返回值进行编码")
    public String encryptToString(byte[] bytes){
        return coderType.encodeToString(encrypt(bytes));
    }

    @Note("使用coderType对返回值进行编码")
    public String encryptToString(String data){
        return coderType.encodeToString(encrypt(data.getBytes(StandardCharsets.UTF_8)));
    }

    public byte[] decrypt(String data){
        return decrypt(coderType.decode(data));
    }

    public String decryptToString(String data){
        return decryptToString(coderType.decode(data));
    }
    public String decryptToString(byte[] bytes){
        return new String(decrypt(bytes),StandardCharsets.UTF_8);
    }
    public Encoder decryptToEncoder(String data){
        return new Encoder(decrypt(data));
    }

    @Note("拼接待签名字符串")
    public static String createLinkString(final Map<String, String> params) {
        return createLinkString(params,"sign" ,"&", false, true);
    }

    @Note("拼接待签名字符串")
    public static String createLinkString(final Map<String, String> params,String exclude, String connStr, boolean urlEncode, boolean skipEmpty) {
        if (LX.isEmpty(params)){
            return "";
        }
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);
        StringBuilder content = new StringBuilder();
        try {
            for (int i = 0; i < keys.size(); i++) {
                String key = keys.get(i);
                String value = params.get(key);
                if (key.equals(exclude) || (skipEmpty && LX.isEmpty(value))){
                    continue;
                }
                if (content.length()>0){
                    content.append(connStr);
                }
                content.append(key).append("=").append(urlEncode ? Url.getCoder().encodeToString(value) : value);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return content.toString();
    }

    static {
        //加密包
        Security.addProvider(new BouncyCastleProvider());
    }
}
